#include "dungeon.h"
#include "layout.h"
#include "dig.h"
#include "util/mt.h"
#include "main.h"

#define PATHS_SIZE 40

typedef struct
{
	Graph* graph;
	Layout* layout;
	Map* map;
	Generator* generator;
	int paths[PATHS_SIZE];
	int n_paths;
} Dungeon;

static void dungeon_layout (Dungeon* dungeon);
static void dungeon_map (Dungeon* dungeon);
static void dungeon_content (Dungeon* dungeon);

static int dungeon_gen_room (Layout* layout, int width, int height, int* root, int n_roots);
static void dungeon_get_room (Layout* layout, int id, Room** room);

void dungeon_generate (Graph* input, Map* output, int dungeon_width, int dungeon_height)
{
	assert(input != NULL);
	assert(output != NULL);
	
	Layout layout;
	layout_init (&layout, dungeon_width, dungeon_height, 1,
	             MAX_TRAVERSALS, graph_count_rooms(input)); // assume 1 room/segment

	Generator generator = { &dungeon_gen_room, &dungeon_get_room };
	Dungeon dungeon = { input, &layout, output, &generator, {}, 0 };

	dungeon_layout (&dungeon);  // generate layout from graph
	dungeon_map (&dungeon);     // generate map from layout
	dungeon_content (&dungeon); // generate map contents
	
	layout_cleanup (&layout);
}

static void dungeon_layout (Dungeon* dungeon)
{
	dig_init (dungeon->layout);

	for (int i = 0; i < dungeon->graph->n_segments; i++) {
		Segment* s = &dungeon->graph->segment[i];
		s->type->layout_generator(s, dungeon->layout, dungeon->generator);
	}

	dig_cleanup ();
}

static void dungeon_map (Dungeon* dungeon)
{
	Map* m = dungeon->map;
	Layout* l = dungeon->layout;
	map_init (m, l->width, l->height, l->layers);
	
	// copy basic tile properties
	for (int z = 0; z < l->layers; z++)
	for (int y = 0; y < l->height; y++)
	for (int x = 0; x < l->width ; x++) {
		Coord c = { x, y, z };
		Tile* t = &TILE(m,x,y,z);
		
		t->coord = c;
		t->index = TILE_INDEX(l,x,y,z);
		
		if (TILEC(l,c).floor) {
			tile_make_floor (t);
		} else {
			tile_make_wall (t);
		}
	}
	
	// traversals
	for (int i = 0; i < l->n_traversals; i++) {
		Traversal* trav = &l->traversal[i];
		Coord a = trav->anchor[0];
		Coord b = trav->anchor[1];
		Tile* ta = &TILEC (m, a);
		Tile* tb = &TILEC (m, b);
			
		if (trav->type == TRAVERSAL_TELEPORTER) {
			tile_make_teleporter	(ta, b);
			tile_make_teleporter	(tb, a);
		} else { // TRAVERSAL_STAIRS
			tile_make_stairs (ta, direction_primary (a, b), b);
			tile_make_stairs (tb, direction_primary (b, a), a);
		}
	}
	
	// rooms
	for (int i = 0; i < l->n_rooms; i++) {
		Rect* r = &l->room[i].area;
		for (int y = r->top ; y < r->bottom; y++)
		for (int x = r->left; x < r->right ; x++) {
			TILE(m,x,y,r->z).is_room = true;
			TILE(m,x,y,r->z).room = i;
		}
	}
}

/*
 * Applies segment-specific content generation algorithms
 * to the map, which makes the dungeon playable.
 */
static void dungeon_content (Dungeon* dungeon)
{
	for (int i = 0; i < dungeon->graph->n_segments; i++) {
		Segment* s = &dungeon->graph->segment[i];
		Room* room_in = &dungeon->layout->room[s->room[s->n_rooms > 1 ? 1 : 0]];
		Room* room_out = &dungeon->layout->room[s->room[0]];
		s->type->content_generator (s, dungeon->layout, dungeon->map,
		                            room_in->entry, room_in->n_entries,
		                            room_out->exit, room_out->n_exits);
	}
}

static int dungeon_gen_room (Layout* layout, int width, int height, int roots[], int n_roots)
{
	return dig_room (width, height, roots, n_roots);
}

static void dungeon_get_room (Layout* layout, int id, Room** room)
{
	*room = &layout->room[id];
}

#ifdef TEST_DUNGEON
#include <stdio.h>
#include "util/algorithm.h"

static void print_segment (Graph* graph, Segment* s)
{
	printf ("%zd:%s ", s - graph->segment, s->type->name);
}

static void print_graph (Graph* graph)
{
	for (int i = 0; i < graph->n_segments; i++)
	{
		Segment* s = &graph->segment[i];
		for (int j = 0; j < s->n_roots; j++)
		{
			print_segment (graph, s->root[j]);
		}
		printf ("-> * ");
		print_segment (graph, s);
		printf ("* -> ");
		for (int j = 0; j < s->n_branches; j++)
		{
			print_segment (graph, s->branch[j]);
		}
		printf ("\n");
	}
}


void print_map (Map* map, const char* filename)
{
	FILE* out = filename ? NULL : stdout;
	char* fnbuf = filename ? malloc(strlen(filename) + 10) : NULL;
	
	printf ("Map with %dx%dx%d tiles **********************\n", map->width, map->height, map->layers);
	for (int z = 0; z < map->layers; z++) {
		if (filename) {
			sprintf (fnbuf, "%s_%d.txt", filename, z);
			out = fopen (fnbuf, "w");
		}
		fprintf (out, "-- Layer %d --\n", z);
		for (int y = 0; y < map->height; y++) {
			for (int x = 0; x < map->width; x++) {
				Tile* t = TILE (map, x, y, z);
				char c = ' ';
				switch (t->type) {
					case TILE_STANDARD:
						if (t->is_blocking) {
							if (t->room >= 0) {
								c = '=';
							} else {
								c = '#';
							}
						} else {
							if (t->room >= 0) {
								Coord mid = rect_middle (&map->rooms[t->room]);
								if (COORDS_EQUAL (mid, ((Coord) {x,y,z}))) {
									if (t->room < 10) c = '0' + t->room;
									else c = 'A' + t->room -10;
								}
							}
						}
						break;
						
					case TILE_STAIRS:
						if (t->stairs.dir == DIR_LEVELUP)   c = '<'; else 
						if (t->stairs.dir == DIR_LEVELDOWN) c = '>'; else
						                                    c = '/';
						break;
					
					case TILE_TELEPORTER:
						c = '@';
						break;
					
					case TILE_DOOR:
						if (t->door.orientation == DOOR_HORIZONTAL) c = '=';
						else                                        c = '|';
						break;
					
					case TILE_TRIGGER:
						c = ',';
						break;
				}
				fprintf (out, "%c ", c);
			}
			fprintf (out, "\n");
		}
		fprintf (out, "\n");
		if (filename) {
			fclose(out);
		}
	}
	
	free(fnbuf);
}

int main (int argc, char* argv[])
{
	Graph graph;
	Map map;

	algorithm_init (1000000);

	int n_players = 4;
	int seed = 0;
	int extent = 70;
	graph_init (&graph, n_players, extent);
	graph_generate (&graph, n_players, seed);

	print_graph (&graph);

	dungeon_generate (&graph, &map, 74, 74);
	
	print_map (&map, argc > 1 ? argv[1] : NULL);

	graph_cleanup (&graph);
	map_cleanup (&map);
	return 0;
}
#endif
